package com.effectiv.gooruda.rest.service.impl;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import org.springframework.stereotype.Component;

import com.effectiv.gooruda.rest.domain.Profile;
import com.effectiv.gooruda.rest.domain.Role;
import com.effectiv.gooruda.rest.domain.User;

import com.effectiv.gooruda.rest.ext.ExtResponse;
import com.effectiv.gooruda.rest.ext.PasswordEncoder;
import com.effectiv.gooruda.rest.service.api.RoleService;
import com.effectiv.gooruda.rest.service.api.SessionService;
import com.effectiv.gooruda.rest.service.api.UserService;

//ERROR CODES - 200-250

@Slf4j
@Component
public class UserServiceImpl extends UserService {

	@Autowired
	private RoleService roleService;

	@Autowired
	@Qualifier("sessionServiceImpl")
	private SessionService sessionService;

	public Response save(User user) {
		String userName = user.getUserName();

		List<Role> roles = user.getRoles();

		user.setRoles(null);

		User u = findByUserNameInternal(userName);

		if (u != null) {
			throwException(200, "User with user name - " + userName
					+ " already exists.");
		}

		String password = PasswordEncoder.encode(user.getPassword(), getSalt());

		user.setPassword(password);

		getTemplate().save(user, getCollection());

		if (roles != null) {
			List<String> roleNames = new ArrayList<String>();

			for (Role r : roles) {
				roleNames.add(r.getName());
			}

			user = addAllRoles(userName, roleNames);
		}
		
		ExtResponse res = new ExtResponse();
		res.setId(user.getId());
		res.setMessage("User saved successfully.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
		
		
	}
	
	public Response addRoles(String userName, List<String> roleNames){
		addAllRoles(userName, roleNames);
		
		ExtResponse res = new ExtResponse();
		res.setMessage("Roles added successfully.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}
	
	private User addAllRoles(String userName, List<String> roles) {
		User u = findByUserNameInternal(userName);

		if (u == null) {

			throwException(201, "User with name - " + userName + " not found.");
		}

		List<Role> existingRoles = u.getRoles();

		if (existingRoles == null) {
			existingRoles = new ArrayList<Role>();
		}

		for (String r : roles) {
			Role role = roleService.findByName(r);

			log.debug("Retrieved role = {}", role);

			if (role == null) {
				throwException(202, "No role found for role name - " + r);
			}

			if (existingRoles.contains(role)) {
				log.debug("Role already attached to user = {}", role);

				throwException(203, "Role - " + r
						+ " already associated with user - " + userName);
			}
			existingRoles.add(role);

		}

		log.debug("Roles before saving = {}", existingRoles);

		u.setRoles(existingRoles);

		getTemplate().save(u, getCollection());

		return u;
	}

	public Response addRole(String userName, String role) {
		
		log.info("User name = {}", userName);
		log.info("role = {}", role);
		
		
		// check if role exists
		Role r = roleService.findByName(role);

		if (r == null) {
			throwException(202, "No role found for role name - " + role);
		}

		User u = findByUserNameInternal(userName);

		if (u == null) {
			throwException(201, "User with name - " + userName + " not found.");
		}

		List<Role> roles = u.getRoles();
		if (roles == null) {
			roles = new ArrayList<Role>();
		}

		if (roles.contains(r)) {

			throwException(203, "Role - " + role
					+ " already attached to user - " + userName);
		}

		roles.add(r);
		u.setRoles(roles);

		getTemplate().save(u, getCollection());

		ExtResponse res = new ExtResponse();
		res.setId(u.getId());
		res.setMessage("Roles added to user successfully.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}
	
	private User findByUserNameInternal(String userName){
		Query query = new Query();
		query.addCriteria(Criteria.where("userName").is(userName));

		User u = getTemplate().findOne(query, User.class, getCollection());
		return u;
	}
	
	public Response findByUserName(String userName) {
		

		User u = findByUserNameInternal(userName);

		ExtResponse res = new ExtResponse();
		res.setValue(u);
		
		if(u != null){
			res.setMessage("User found.");
		}
		else{
			res.setMessage("User not found.");
		}
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}
	
	public Response authenticate(String userName, String password, boolean startSession){
		
		User u = authenticateInternal(userName, password);
		
		if(startSession){
			sessionService.create(u);
		}
		
		ExtResponse res = new ExtResponse();
		res.setId(u.getId());
		res.setValue(true);
		res.setMessage("User authenticated successfully.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
		
	}
	
	private User authenticateInternal(String userName, String password) {
		User u = findByUserNameInternal(userName);

		if (u == null) {

			throwException(204, "Invalid user name - " + userName);
		}

		if (!PasswordEncoder.isPasswordValid(u.getPassword(), password,
				getSalt())) {
			throwException(205, "Invalid password.");
		}
		
		if(u.isLocked()){
			throwException(206, "User account locked.");
		}
		

		return u;
	}

	public Response changePassword(String userName, String oldPassword,
			String newPassword) {
		User user = authenticateInternal(userName, oldPassword);

		String password = PasswordEncoder.encode(newPassword, getSalt());

		user.setPassword(password);

		getTemplate().save(user, getCollection());

		ExtResponse res = new ExtResponse();
		res.setId(user.getId());
		res.setValue(true);
		res.setMessage("Password changed successfully.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}

	

	public Response upsertProfile(String userName, Profile profile) {
		User user = findByUserNameInternal(userName);
		if (user == null) {

			throwException(201, "User with name - " + userName + " not found.");
		}
		
		user.setProfile(profile);
		getTemplate().save(user, getCollection());

		ExtResponse res = new ExtResponse();
		res.setId(user.getId());
		res.setMessage("Profile add/update successful.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}

	public Response deleteUser(String userName) {
		Query query = new Query();
		query.addCriteria(Criteria.where("userName").is(userName));

		
		Update update = new Update();
		update.addToSet("deleted", true);
		
		getTemplate().updateFirst(query, update, getCollection());
		
		ExtResponse res = new ExtResponse();
		res.setMessage("User with user name " + userName + " deleted.");
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}

	public List<User> findAll(int pageNo, int pageSize) {
		Query query = new Query();
		query.skip(pageNo * pageSize);
		query.limit(pageSize);
		return getTemplate().find(query, User.class, getCollection());
	}

	public long count() {
		return getTemplate().count(new Query(), getCollection());

	}

	public List<User> findLockedUsers(int pageNo, int pageSize) {
		Query query = new Query();
		query.addCriteria(Criteria.where("locked").is(true));
		query.skip(pageNo * pageSize);
		query.limit(pageSize);
		return getTemplate().find(query, User.class, getCollection());
	}

	public long countLockedUsers() {
		Query query = new Query();
		query.addCriteria(Criteria.where("locked").is(true));
		return getTemplate().count(query, getCollection());
	}

	public List<Role> findRolesForUser(String userName) {
		User u = findByUserNameInternal(userName);
		return u.getRoles();
	}

	public User findOne(String id) {
		return getTemplate().findById(id, User.class, getCollection());

	}

	public User findByEmail(String email) {
		Query query = new Query();
		query.addCriteria(Criteria.where("email").is(email));
		return getTemplate().findOne(query, User.class, getCollection());
	}

	public User lock(String userName) {
		User u = findByUserNameInternal(userName);
		u.setLocked(true);
		getTemplate().save(u, getCollection());
		return u;
	}

	public User unlock(String userName) {
		User u = findByUserNameInternal(userName);
		u.setLocked(false);
		getTemplate().save(u, getCollection());
		return u;
	}

	public Response removeAllRoles(String userName) {
		User u = findByUserNameInternal(userName);
		u.setRoles(null);
		getTemplate().save(u, getCollection());
		
		ExtResponse res = new ExtResponse();
		res.setMessage("All roles removed for user - " + userName);
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}

	public Response updateEmail(String userName, String newEmail) {
		User u = findByUserNameInternal(userName);
		u.setEmail(newEmail);
		getTemplate().save(u, getCollection());
		
		
		ExtResponse res = new ExtResponse();
		res.setId(u.getId());
		
		res.setMessage("Email updated successfully." + userName);
		
		GenericEntity<ExtResponse> entity = new GenericEntity<ExtResponse>(res, ExtResponse.class);
		
		ResponseBuilder builder = Response.ok(entity);
		
		return builder.build();
	}

}
